package com.md.value.html;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.md.value.UnitConstants.DAX_TO_PT;
import static com.md.value.UnitConstants.DPI;
import static com.md.value.UnitConstants.EMUS_PER_CENTIMETER;
import static com.md.value.UnitConstants.EMU_TO_PT;
import static com.md.value.UnitConstants.POINTS;

/**
 * @author 刘骄阳
 * 2024-05-24 17:32
 */
@AllArgsConstructor
@Getter
public enum Unit {
  EM(0, "em"),
  PX(1, "px"),
  PT(2, "pt"),
  DXA(3, "dxa"),
  PCT(4, "%"),
  EMU(5, "emu");

  static final Map<String, Unit> ENUM_MAP_STRING = Arrays.stream(Unit.values())
    .collect(Collectors.toMap(Unit::getValue, e -> e));
  private final Integer code;
  private final String value;

  public static Unit of(final String styleName) {
    return ENUM_MAP_STRING.get(styleName);
  }

  public static BigDecimal convertDxaToPx(final Double dxa) {
    // 1 DXA = 1/20 PT
    BigDecimal dxaValue = BigDecimal.valueOf(dxa);
    BigDecimal pt = dxaValue.divide(DAX_TO_PT, MathContext.DECIMAL128);

    // 1 PT ≈ 1.333 PX
    BigDecimal conversionFactor = new BigDecimal("1.333");
    return pt.multiply(conversionFactor).setScale(2, RoundingMode.HALF_UP);
  }

  public static BigDecimal convertPTToPx(final Double pt) {
    BigDecimal conversionFactor = new BigDecimal("1.333");
    return BigDecimal.valueOf(pt).multiply(conversionFactor).setScale(2, RoundingMode.HALF_UP);
  }

  /**
   * 将EMU转换为像素
   *
   * @param emu EMU值
   * @return 像素值
   */
  public static BigDecimal convertEmuToPx(final Double emu) {
    // 计算EMU到px
    return BigDecimal.valueOf(emu).divide(EMUS_PER_CENTIMETER, MathContext.DECIMAL128)
      .multiply(DPI.divide(new BigDecimal("2.54"), MathContext.DECIMAL128))
      .setScale(2, RoundingMode.HALF_UP);
  }

  private Function<Double, BigDecimal> convert(final Unit unit) {
    switch (this) {
      case PT:
        switch (unit) {
          case PX:
            return Unit::convertPTToPx;
          default:
            break;
        }
        break;
      case PX:
        switch (unit) {
          case PT:
            return number -> BigDecimal.valueOf(number).divide(DPI, MathContext.DECIMAL128).multiply(POINTS)
              .setScale(2, RoundingMode.HALF_UP);
          default:
            break;
        }
        break;
      case DXA:
        switch (unit) {
          case PT:
            return number -> BigDecimal.valueOf(number).divide(DAX_TO_PT, MathContext.DECIMAL128)
              .setScale(2, RoundingMode.HALF_UP);
          case PX:
            return Unit::convertDxaToPx;
          default:
            break;
        }
        break;
      case EMU:
        switch (unit) {
          case PT:
            return number -> BigDecimal.valueOf(number).divide(EMU_TO_PT, MathContext.DECIMAL128)
              .setScale(2, RoundingMode.HALF_UP);
          case PX:
            return Unit::convertEmuToPx;
          default:
            break;
        }
        break;
      default:
        return BigDecimal::valueOf;
    }
    return BigDecimal::valueOf;
  }

  public BigDecimal convert(final Unit unit, final Double number) {
    return convert(unit).apply(number);
  }
}
